"""
景点数据去重和合并管理命令
解决景点重复问题，如"南昌八一纪念馆"和"南昌八一纪念馆-叶挺"
"""
from django.core.management.base import BaseCommand
from django.db.models import Q
from django.core.cache import cache
from apps.attractions.models import Attraction
from decimal import Decimal
import re


class Command(BaseCommand):
    help = '景点数据去重和合并'

    def add_arguments(self, parser):
        parser.add_argument(
            '--dry-run',
            action='store_true',
            help='仅显示将要执行的操作，不实际执行'
        )
        parser.add_argument(
            '--distance-threshold',
            type=float,
            default=0.5,
            help='距离阈值（公里），小于此距离的景点视为重复，默认0.5公里'
        )

    def handle(self, *args, **options):
        dry_run = options['dry_run']
        distance_threshold = options['distance_threshold']
        
        self.stdout.write(self.style.SUCCESS('=' * 60))
        self.stdout.write(self.style.SUCCESS('开始景点数据去重和合并'))
        self.stdout.write(self.style.SUCCESS('=' * 60))
        self.stdout.write(f'距离阈值: {distance_threshold} 公里')
        self.stdout.write(f'模式: {"预览模式（不实际执行）" if dry_run else "执行模式"}')
        self.stdout.write('')

        # 1. 名称相似度去重
        self.stdout.write(self.style.WARNING('步骤1: 基于名称相似度的去重'))
        self.deduplicate_by_name_similarity(dry_run)
        
        # 2. 位置相近去重
        self.stdout.write(self.style.WARNING('\n步骤2: 基于位置相近的去重'))
        self.deduplicate_by_location(dry_run, distance_threshold)
        
        # 3. 合并子景点（如"XX-YY"格式）
        self.stdout.write(self.style.WARNING('\n步骤3: 合并子景点'))
        self.merge_sub_attractions(dry_run)
        
        # 清除缓存
        if not dry_run:
            self.stdout.write(self.style.WARNING('\n清除景点数据缓存...'))
            try:
                # 清除列表缓存
                if hasattr(cache, 'delete_pattern'):
                    cache.delete_pattern('attractions_list_*')
                    cache.delete_pattern('attraction_detail_*')
                    cache.delete_pattern('attraction_recommendations_*')
                    self.stdout.write(self.style.SUCCESS('✓ 已清除景点相关缓存'))
                else:
                    # 如果不支持模式匹配，清除所有缓存
                    cache.clear()
                    self.stdout.write(self.style.SUCCESS('✓ 已清除所有缓存'))
            except Exception as e:
                self.stdout.write(self.style.WARNING(f'清除缓存时出错: {e}'))
        
        self.stdout.write(self.style.SUCCESS('\n' + '=' * 60))
        self.stdout.write(self.style.SUCCESS('去重和合并完成！'))
        self.stdout.write(self.style.SUCCESS('=' * 60))

    def calculate_distance(self, lat1, lon1, lat2, lon2):
        """计算两点之间的距离（公里）"""
        from math import radians, cos, sin, asin, sqrt
        
        # 转换为弧度
        lat1, lon1, lat2, lon2 = map(radians, [float(lat1), float(lon1), float(lat2), float(lon2)])
        
        # Haversine公式
        dlat = lat2 - lat1
        dlon = lon2 - lon1
        a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
        c = 2 * asin(sqrt(a))
        r = 6371  # 地球半径（公里）
        
        return c * r

    def name_similarity(self, name1, name2):
        """计算两个名称的相似度（0-1之间）"""
        # 移除常见后缀和标点
        def normalize(name):
            name = name.strip()
            # 移除常见后缀
            name = re.sub(r'[-_].*$', '', name)  # 移除"-XX"格式的后缀
            name = re.sub(r'\(.*?\)', '', name)  # 移除括号内容
            name = re.sub(r'（.*?）', '', name)  # 移除中文括号内容
            return name.strip()
        
        norm1 = normalize(name1)
        norm2 = normalize(name2)
        
        # 如果规范化后的名称相同，相似度为1
        if norm1 == norm2:
            return 1.0
        
        # 如果一个是另一个的子串，相似度较高
        if norm1 in norm2 or norm2 in norm1:
            return 0.8
        
        # 计算字符重叠度
        set1 = set(norm1)
        set2 = set(norm2)
        if len(set1) == 0 or len(set2) == 0:
            return 0.0
        
        intersection = len(set1 & set2)
        union = len(set1 | set2)
        return intersection / union if union > 0 else 0.0

    def deduplicate_by_name_similarity(self, dry_run):
        """基于名称相似度去重"""
        attractions = Attraction.objects.all().order_by('id')
        duplicates = []
        processed_ids = set()
        
        for i, attr1 in enumerate(attractions):
            if attr1.id in processed_ids:
                continue
                
            similar_attrs = []
            for attr2 in attractions[i+1:]:
                if attr2.id in processed_ids:
                    continue
                
                similarity = self.name_similarity(attr1.name, attr2.name)
                if similarity >= 0.7:  # 相似度阈值
                    # 检查位置是否相近
                    distance = self.calculate_distance(
                        attr1.latitude, attr1.longitude,
                        attr2.latitude, attr2.longitude
                    )
                    if distance < 1.0:  # 1公里内
                        similar_attrs.append((attr2, similarity, distance))
            
            if similar_attrs:
                # 选择保留的景点（评分高、热度高、信息完整）
                candidates = [attr1] + [attr[0] for attr in similar_attrs]
                best = max(candidates, key=lambda a: (
                    a.rating or 0,
                    a.popularity or 0,
                    len(a.description or ''),
                    len(a.address or '')
                ))
                
                to_merge = [a for a in candidates if a.id != best.id]
                if to_merge:
                    duplicates.append((best, to_merge))
                    processed_ids.update([a.id for a in to_merge])
                    processed_ids.add(best.id)
        
        # 显示和合并
        merged_count = 0
        for keep, merge_list in duplicates:
            self.stdout.write(f'\n保留: {keep.name} (ID: {keep.id})')
            for attr in merge_list:
                self.stdout.write(f'  合并: {attr.name} (ID: {attr.id})')
                if not dry_run:
                    # 迁移数据
                    self.migrate_data(keep, attr)
                    attr.delete()
                merged_count += 1
        
        self.stdout.write(self.style.SUCCESS(f'\n共合并 {merged_count} 个重复景点'))

    def deduplicate_by_location(self, dry_run, distance_threshold):
        """基于位置相近去重"""
        attractions = Attraction.objects.all().order_by('id')
        duplicates = []
        processed_ids = set()
        
        for i, attr1 in enumerate(attractions):
            if attr1.id in processed_ids:
                continue
            
            nearby_attrs = []
            for attr2 in attractions[i+1:]:
                if attr2.id in processed_ids:
                    continue
                
                distance = self.calculate_distance(
                    attr1.latitude, attr1.longitude,
                    attr2.latitude, attr2.longitude
                )
                
                if distance < distance_threshold:
                    # 检查名称是否相似
                    similarity = self.name_similarity(attr1.name, attr2.name)
                    if similarity >= 0.5:  # 名称相似度阈值
                        nearby_attrs.append((attr2, distance, similarity))
            
            if nearby_attrs:
                candidates = [attr1] + [attr[0] for attr in nearby_attrs]
                best = max(candidates, key=lambda a: (
                    a.rating or 0,
                    a.popularity or 0,
                    len(a.description or ''),
                    len(a.address or '')
                ))
                
                to_merge = [a for a in candidates if a.id != best.id]
                if to_merge:
                    duplicates.append((best, to_merge))
                    processed_ids.update([a.id for a in to_merge])
                    processed_ids.add(best.id)
        
        # 显示和合并
        merged_count = 0
        for keep, merge_list in duplicates:
            self.stdout.write(f'\n保留: {keep.name} (ID: {keep.id})')
            for attr in merge_list:
                self.stdout.write(f'  合并: {attr.name} (ID: {attr.id})')
                if not dry_run:
                    self.migrate_data(keep, attr)
                    attr.delete()
                merged_count += 1
        
        self.stdout.write(self.style.SUCCESS(f'\n共合并 {merged_count} 个位置相近的重复景点'))

    def merge_sub_attractions(self, dry_run):
        """合并子景点（如"XX-YY"格式）"""
        # 查找所有可能是子景点的记录（名称包含"-"或"（"）
        pattern = re.compile(r'^(.+?)[-_（(].+')
        
        sub_attractions = []
        for attr in Attraction.objects.all():
            match = pattern.match(attr.name)
            if match:
                parent_name = match.group(1).strip()
                # 查找可能的父景点
                parent = Attraction.objects.filter(name=parent_name).first()
                if parent and parent.id != attr.id:
                    # 检查位置是否相近
                    distance = self.calculate_distance(
                        parent.latitude, parent.longitude,
                        attr.latitude, attr.longitude
                    )
                    if distance < 1.0:  # 1公里内
                        sub_attractions.append((parent, attr, distance))
        
        # 合并
        merged_count = 0
        processed_ids = set()
        for parent, sub, distance in sub_attractions:
            if sub.id in processed_ids:
                continue
            
            self.stdout.write(f'\n父景点: {parent.name} (ID: {parent.id})')
            self.stdout.write(f'  子景点: {sub.name} (ID: {sub.id}, 距离: {distance:.2f}km)')
            
            if not dry_run:
                self.migrate_data(parent, sub)
                sub.delete()
                processed_ids.add(sub.id)
            
            merged_count += 1
        
        self.stdout.write(self.style.SUCCESS(f'\n共合并 {merged_count} 个子景点'))

    def migrate_data(self, keep, merge):
        """将merge景点的数据迁移到keep景点"""
        # 迁移评价
        for review in merge.reviews.all():
            review.attraction = keep
            review.save()
        
        # 迁移收藏
        for favorite in merge.favorited_by.all():
            # 检查是否已收藏
            if not keep.favorited_by.filter(user=favorite.user).exists():
                favorite.attraction = keep
                favorite.save()
            else:
                favorite.delete()
        
        # 迁移图片
        for image in merge.images.all():
            image.attraction = keep
            image.save()
        
        # 更新评分（重新计算）
        from django.db.models import Avg
        avg_rating = keep.reviews.aggregate(avg=Avg('rating'))['avg']
        if avg_rating:
            keep.rating = round(avg_rating, 2)
        
        # 更新热度（取较大值）
        keep.popularity = max(keep.popularity or 0, merge.popularity or 0)
        
        # 合并描述（如果keep的描述为空或较短）
        if not keep.description or len(keep.description) < len(merge.description or ''):
            keep.description = merge.description
        
        # 合并地址（如果keep的地址为空）
        if not keep.address and merge.address:
            keep.address = merge.address
        
        # 合并高德地图数据
        if not keep.amap_poi_id and merge.amap_poi_id:
            keep.amap_poi_id = merge.amap_poi_id
        
        if merge.amap_image_urls:
            existing_images = set(keep.amap_image_urls or [])
            new_images = set(merge.amap_image_urls)
            keep.amap_image_urls = list(existing_images | new_images)
        
        keep.save()

